package com.up.vms.interfaces.rest.intercepters;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.up.vms.domain.shared.App;
import com.up.vms.domain.shared.AppService;
import com.up.vms.infrastructure.exception.AccessSignErrorException;
import com.up.vms.infrastructure.exception.AppUnauthorizedException;
import com.up.vms.infrastructure.exception.UbpFrameException;
import com.up.vms.infrastructure.mvc.dto.HttpHeaders;
import com.up.vms.infrastructure.security.Sign;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.Map;
import java.util.Map.Entry;

/**
 * 验证http header 信息
 *
 * @author Andy
 * @since 2016-12-07 08:47
 */
@Component
@NoArgsConstructor
@Slf4j
public class AccessRequiredInterceptor extends HandlerInterceptorAdapter {


    @Value("${business.isAccessRequred}")
    private boolean isAccessRequred;

    @Resource
    private AppService appService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        if (!isAccessRequred) {
            try {
                handleRequest(request);
            } catch (Exception e) {
                throw e;
            }
            return true;
        }
        AccessRequired annotation = null;
        if (handler instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod) handler;
            annotation = method.getMethodAnnotation(AccessRequired.class);
        }

        if (annotation == null) {
            try {
                handleRequest(request);
            } catch (Exception e) {
                throw e;
            }
            return true;
        }

        try {
            if (checkHeader(request)) {
                return true;
            } else {
                throw new AccessSignErrorException("请检查参数签名算法");
            }
        } catch (AccessSignErrorException | AppUnauthorizedException e) {
            throw e;
        } catch (Exception e) {
            log.warn("get Error in Api Access preHandle : {}" , e);
            throw new AccessSignErrorException(e.getMessage());
        }

    }

    public boolean checkHeader(HttpServletRequest request) throws Exception {
        checkHeaderNotNull(request, HttpHeaders.X_EDI_SIGNATURE);
        checkHeaderNotNull(request, HttpHeaders.X_EDI_CLIENT_KEY);
        checkHeaderNotNull(request, HttpHeaders.X_EDI_REQUEST_ID);
        checkHeaderNotNull(request, HttpHeaders.X_EDI_STATGE);
        String gatewaySign = request.getHeader(HttpHeaders.X_EDI_SIGNATURE);

        String exceptedSign = calcSign(request);
        log.info("Server sign Code: " + exceptedSign);
        return exceptedSign.equals(gatewaySign);
    }

    private void checkHeaderNotNull(HttpServletRequest request, String headerName) {
        String headerValue = request.getHeader(headerName);
        log.info("Client "+headerName+" : " + headerValue);
        Preconditions.checkArgument(StringUtils.isNotBlank(headerValue), "%s 不能为空", headerName);
    }

    private String calcSign(HttpServletRequest request) throws UnsupportedEncodingException, UbpFrameException {
        
        String uri = URLDecoder.decode(request.getRequestURI(), "UTF-8");
        log.info("Client URI: " + uri);
        String httpMethod = request.getMethod().toUpperCase();
        log.info("Client httpMethod: " + httpMethod);

        ImmutableMap.Builder<String, String> headerBuilder = ImmutableMap.<String, String>builder();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            headerBuilder.put(headerName, request.getHeader(headerName));
        }

        ImmutableMap.Builder<String, Object> paramBuilder = ImmutableMap.builder();
        Map<String, String[]> map = request.getParameterMap();
        log.info("Client parameter size :" + map.size());
        for(Entry<String, String[]> entry :  map.entrySet()) {
            String key= entry.getKey();
            log.info("Client parameter "+key+":");
            paramBuilder.put(key, entry.getValue());
        }

        byte[] inputStreamBytes = Sign.getRequestInputStream(request);
        log.info("Client inputStreamBytes: " + new String(inputStreamBytes,"UTF-8"));

        App app = appService.getAppByAppId(request.getHeader(HttpHeaders.X_EDI_CLIENT_KEY));
        try {
            Preconditions.checkNotNull(app, "未授权的应用");
        } catch (NullPointerException e) {
            throw new AppUnauthorizedException(e.getMessage());
        }
        return Sign.serviceSign(uri, httpMethod, app.getAccessSecret(), headerBuilder.build(), paramBuilder.build(), inputStreamBytes);
    }

    private void handleRequest(HttpServletRequest request) throws UnsupportedEncodingException {
        String uri = URLDecoder.decode(request.getRequestURI(), "UTF-8");
        log.info("Client URI: " + uri);
        String httpMethod = request.getMethod().toUpperCase();
        log.info("Client httpMethod: " + httpMethod);

        ImmutableMap.Builder<String, String> headerBuilder = ImmutableMap.<String, String>builder();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            headerBuilder.put(headerName, request.getHeader(headerName));
        }

        ImmutableMap.Builder<String, Object> paramBuilder = ImmutableMap.builder();
        Map<String, String[]> map = request.getParameterMap();
        log.info("Client parameter size :" + map.size());
        for(Entry<String, String[]> entry :  map.entrySet()) {
            String key= entry.getKey();
            log.info("Client parameter "+key+":");
            paramBuilder.put(key, entry.getValue());
        }

        byte[] inputStreamBytes = Sign.getRequestInputStream(request);
        log.info("Client inputStreamBytes: " + new String(inputStreamBytes,"UTF-8"));

    }
}
